-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #21868, #21869, and #21870: handle CapsOf in more places #21875
Conversation
@@ -198,7 +198,7 @@ extension (tp: Type) | |||
|| tp.isRootCapability | |||
) && !tp.symbol.isOneOf(UnstableValueFlags) | |||
case tp: TypeRef => | |||
tp.symbol.isAbstractOrParamType && tp.derivesFrom(defn.Caps_CapSet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@odersky Do you remember why we require the arguments of CapsOf
to be abstract before? I think any type derived from CapSet should work.
Strange, we're consistently getting a CI failure |
If we do not strip the capture set at this point, then the type will be transformed twice and we'll get the same error message twice.
def boom(): Unit^{C^} // error | ||
|
||
trait Abstract: | ||
type C <: CapSet^ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idealy, we'd like to retrict the capture parameter and member bounded by >: CapSet^{} <: CapSet^
, which can forbid instantiating to meaningless types, like Nothing
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's certainly cleaner. The question is how far we want to take it for now. A fully general solution would also have to deal with the presence of explicit lower bounds, which could be themselves type variables, etc.
trait Abstract[L >: CapSet^{}]:
type T >: L <: CapSet^
Maybe let's track this in a separate issue? The point of the current fix is to prevent the uncaught exception and compiler crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is an issue, as long as L
is also bounded by CapSet
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the below examples show, there's a still a few quirks. I'm also somewhat uneasy with automatically presuming that the lower bound of a CapSet^
-bounded type variable is CapSet^{}
. E.g., should this always be the case, or only with cc enabled?
Since the specific issues are resolved, let's merge this and have a separate issue to make capture set intervals and subtyping airtight.
At some point, we should probably make intersection and unions work as well: class Concrete2 extends Abstract:
type C = CapSet^{} & CapSet^{}
def boom() = () // error
class Concrete3 extends Abstract:
type C = CapSet^{} | CapSet^{}
def boom() = () // error It's not unreasonable to expect |
OK, so this is weird: import language.experimental.captureChecking
import caps.*
trait Abstract[X >: Nothing]: // Concrete2 and 3 fail with this lower bound, but work with X >: CapSet^{}
type C >: X <: CapSet^
def boom(): Unit^{C^}
class Concrete extends Abstract[CapSet^{}]:
type C = CapSet^{}
def boom() = () // ok
class Concrete2 extends Abstract[CapSet^{}]:
type C = CapSet^{} & CapSet^{}
def boom() = () // error
class Concrete3 extends Abstract[CapSet^{}]:
type C = CapSet^{} | CapSet^{}
def boom() = () // error We get (now properly reported as an error) "Illegal capture reference ..." as long as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Fix #21868, #21869, and #21870